<?php
namespace Hilaoyu\ComputeCloudClient\ctyun;

use Fernet\Fernet;
use Hilaoyu\Utils\UtilArr;
use Hilaoyu\Utils\UtilHttp;
use Hilaoyu\Utils\UtilStr;
use Hilaoyu\Utils\UtilSupport;


class CtyunClientEcs extends CtyunClient
{
    protected function baseUrl():string{
        return "https://ctecs-global.ctapi.ctyun.cn";
    }

    public function describeRegions($regionName='')
    {
        $regionName = trim($regionName);
        $query = [];
        if($regionName){
            $query['regionName'] = $regionName;
        }

        $result =  $this->request("/v4/region/list-regions",[],$query,"get");
        if(!$result){
            return;
        }
        return (array)UtilArr::get($result, 'returnObj.regionList');
    }

    public function describeZones($regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
            "regionID" => $regionId
        ];

        $result = $this->request("/v4/region/get-zones",[],$query,"get");
        if(!$result){
            return;
        }
        return (array)UtilArr::get($result, 'returnObj.zoneList');
    }

    public function listFlavors(string $flavorName="",$flavorId="",string $azName="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params=[
            "regionID" => $regionId
        ];
        if($azName){
            $params["azName"] = $azName;
        }
        if($flavorName){
            $params["flavorName"] = $flavorName;
        }
        if($flavorId){
            $params["flavorID"] = $flavorId;
        }

        $result = $this->request("/v4/ecs/flavor/list",$params,$query,"post");
        if(!$result){
            return;
        }
        return (array)UtilArr::get($result, 'returnObj.flavorList');
    }

    public function getEcsIdsByOrder(string $orderId)
    {
        $query = [
            "masterOrderID" => $orderId
        ];
        $params=[

        ];

        $ecsIds = [];

        UtilSupport::reTry(function () use (&$ecsIds,$params,$query){
            $result = $this->request("/v4/ecs/order/query-uuid",$params,$query,"get");
            if(!$result){
                $ecsIds = null;
                return true;
            }
            $orderStatus = UtilArr::get($result, 'returnObj.orderStatus');
            if(in_array($orderStatus,[5,11,13,22,0,999])){
                $this->setError("订单状态错误:".$orderStatus);
                return true;
            }
            if(3 == $orderStatus){
                $ecsIds = (array)UtilArr::get($result, 'returnObj.instanceIDList');
                return true;
            }

        },5,60);


        return $ecsIds;
    }


    public function createServers(string $imageId,int $imageType,string $flavorId,string $vpcId,string $subnetId,string $instanceName,string $userPassword,int $bootDiskSize=40,array $secGroupList=[],int $internetBandwidth=0,bool $onDemand=true,string $cycleType="MONTH",int $cycleCount=1,string $azName="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $params=[
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,

            //"projectID" => "0",

            "instanceName" => $instanceName,
            "displayName" => $instanceName,
            "flavorID" => $flavorId,
            "imageID" => $imageId,
            "imageType" => $imageType,
            "bootDiskType" => "SAS",
            "bootDiskSize" => $bootDiskSize,
            "bootDiskIsEncrypt" => false,
            "vpcID" => $vpcId,
            "onDemand" => $onDemand,
            "networkCardList" => [
                [
                    "isMaster" =>true,
                    "subnetID" =>$subnetId,
                ]
            ],

            "extIP" => "0",

            "secGroupList" => $secGroupList,
            //"dataDiskList" => [],

            "userPassword" => $userPassword,

            "autoRenewStatus"=>0,

            //"orderCount" => 1,
            "securityProduct" => "false",
        ];
        //$params["projectID"] = $this->projectId;
        if($azName){
            $params["azName"] =  $azName;
        }
        if(!$onDemand){
            $params["cycleCount"] = $cycleCount;
            $params["cycleType"] = $cycleType;
        }

        /*if($internetBandwidth >0){
            $params["extIP"] = "1";
            $params["ipVersion"] = "ipv4";
            $params["bandwidth"] = $internetBandwidth;
        }*/

        $result = $this->request("/v4/ecs/create-instance",$params,$query,"post");
        if(!$result){
            return;
        }
        $orderId = UtilArr::get($result, 'returnObj.masterOrderID');
        if(!$orderId){
            $this->setError("接口返回订单ID为空");
            return;
        }

        return $this->getEcsIdsByOrder($orderId);
    }

    public function listServers(array $instanceIds,string $vpcId="",string $keyword="",string $azName="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [

        ];
        $pageNo=1;
        $params=[
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        if($azName){
            $params["azName"] = $azName;
        }
        if(!empty($instanceIds)){
            $params["instanceIDList"] = implode(",",$instanceIds);
        }
        if($vpcId){
            $params["vpcID"] = $vpcId;
        }
        if($keyword){
            $params["keyword"] = $keyword;
        }

        $servers = [];
        do{
            $params["pageNo"] = $pageNo;
            $result = $this->request("/v4/ecs/list-instances",$params,$query,"post");
            if(!$result){
                return;
            }
            $servers = array_merge($servers,(array)UtilArr::get($result, 'returnObj.results'));
            $totalPage = UtilArr::get($result, 'returnObj.totalPage');
            if($pageNo >= $totalPage){
                break;
            }
            $pageNo++;
        }while(true);

        return $servers;
    }
    public function batchStopServers(array $instanceIds,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
        ];
        $params=[
            "regionID" => $regionId,
            "instanceIDList" => implode(",",$instanceIds),
            "force" => true,
        ];

        $result = $this->request("/v4/ecs/batch-stop-instances",$params,$query,"post");
        if(!$result){
            return;
        }


        return true;
    }

    public function batchStartServers(array $instanceIds,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [
        ];
        $params=[
            "regionID" => $regionId,
            "instanceIDList" => implode(",",$instanceIds),
        ];

        $result = $this->request("/v4/ecs/batch-start-instances",$params,$query,"post");
        if(!$result){
            return;
        }


        return true;
    }
    public function batchRebootServers(array $instanceIds,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
        ];
        $params=[
            "regionID" => $regionId,
            "instanceIDList" => implode(",",$instanceIds),
            "force"=>true,
        ];

        $result = $this->request("/v4/ecs/batch-reboot-instances",$params,$query,"post");
        if(!$result){
            return;
        }


        return true;
    }

    public function deleteServers(array $instanceIds,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        if(empty($instanceIds)){
            return true;
        }

        foreach ($instanceIds as $instanceId){
            if(!$instanceId){
                continue;
            }
            if(!$this->deleteServerSnapshots($instanceId,$regionId)){
                return false;
            }
        }

        $query = [
        ];
        $params=[
            "clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "instanceIDList" => implode(",",$instanceIds),
        ];


        $result = $this->request("/v4/ecs/batch-delete",$params,$query,"post");
        if(!$result){
            return;
        }


        return true;
    }
    public function resetServerPassword(string $instanceId, string $password,string $username,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $query = [
        ];
        $params=[
            "regionID" => $regionId,
            "instanceID" => $instanceId,
            "newPassword" => $password,
            "userName" => $username,
        ];

        $result = $this->request("/v4/ecs/reset-password",$params,$query,"post");
        if(!$result){
            return;
        }


        return true;
    }
    public function updateServer(string $instanceId, string $name,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);
        $name = trim($name);
        if(!$name){
            return true;
        }
        $query = [
        ];
        $params=[
            "regionID" => $regionId,
            "instanceID" => $instanceId,
            "displayName" => $name,
            "instanceName" => $name,
        ];

        $result = $this->request("/v4/ecs/update-instance",$params,$query,"post");
        if(!$result){
            return;
        }


        return true;
    }
    public function showServerRemoteConsole(string $instanceId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [
            "regionID" => $regionId,
            "instanceID" => $instanceId,
        ];
        $params=[

        ];

        $result = $this->request("/v4/ecs/vnc/details",$params,$query,"get");
        if(!$result){
            return;
        }

        $token = UtilArr::get($result,"returnObj.token");
        if(!defined("NOVNC_URL")){
            define("NOVNC_URL","");
        }
        $url = "";
        if(UtilStr::startsWith($token,"http://") || UtilStr::startsWith($token,"https://")){
            $url = $token;
        }else{
            $url = NOVNC_URL."?autoconnect=1&path=".urlencode($token);
        }

        return $url;
    }
    public function reinstallServer(string $instanceId,string $imageId, string $password,string $username="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "instanceID" => $instanceId,
            "password" => $password,
            "imageID" => $imageId,
        ];
        if($username){
            $params["userName"] = $username;
        }

        $result = $this->request("/v4/ecs/rebuild-instance",$params,$query,"post");
        if(!$result){
            return;
        }


        return (bool)$result;
    }

    public function createServerSnapshot(string $instanceId,string $snapshotName, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "instanceID" => $instanceId,
            "snapshotName" => $snapshotName,
        ];
        $result = $this->request("/v4/ecs/snapshot/create",$params,$query,"post");
        if(!$result){
            return;
        }


        return (bool)$result;
    }
    public function listServerSnapshots(string $instanceId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "instanceID" => $instanceId,
        ];
        $result = $this->request("/v4/ecs/snapshot/list",$params,$query,"post");
        if(!$result){
            return;
        }


        return UtilArr::get($result,"returnObj.results");
    }
    public function deleteServerSnapshots(string $instanceId, string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $snapshots = (array)$this->listServerSnapshots($instanceId);
        if(empty($snapshots)){
            return true;
        }

        $snapshotIds = [];
        foreach ($snapshots as $snapshot){
            $snapshotId = UtilArr::get($snapshot,"snapshotID");
            if(!$snapshotId){
                continue;
            }
            $snapshotIds[] = $snapshotId;
        }
        if(empty($snapshotIds)){
            return true;
        }

        $query = [

        ];
        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "snapshotIDList" => implode(",",$snapshotIds),
        ];
        $result = $this->request("/v4/ecs/snapshot/batch-delete",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }


    protected function cloudAssistantEncodeCommandContent(string $commandContent):string
    {
        $content = time() .":" . base64_encode($commandContent);
        $enContent = $this->fernet->encode($content);
        return $enContent;
    }
    public function cloudAssistantCreateCommand(string $commandContent,string $commandName,string $commandType="Shell", string $regionId="")
    {
        if(!$commandContent){
            $this->setError("命令内容不能为空");
            return;
        }
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];



        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "commandName" => $commandName,
            "commandType" => $commandType,
            "commandContent" => $this->cloudAssistantEncodeCommandContent($commandContent),
            "enabledParameter" => false,
        ];
        $result = $this->request("/v4/cloud-assistant/create-command",$params,$query,"post");
        if(!$result){
            return;
        }

        return UtilArr::get($result,"returnObj.commandID");
    }
    public function cloudAssistantListCommands(string $commandId="",string $commandName="",string $commandType="", string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];

        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        $filters = [];
        if($commandId){
            $filters[] = [
                "key"=>"commandID",
                "value" => $commandId
            ];
        }
        if($commandName){
            $filters[] = [
                "key"=>"commandName",
                "value" => $commandName
            ];
        }
        if($commandType){
            $filters[] = [
                "key"=>"commandType",
                "value" => $commandType
            ];
        }
        if(!empty($filters)){
            $params["filters"] = $filters;
        }

        $result = $this->request("/v4/cloud-assistant/get-commands",$params,$query,"post");
        if(!$result){
            return;
        }

        return UtilArr::get($result,"returnObj.commands");
    }
    public function cloudAssistantDeleteCommand(string $commandId, string $regionId="")
    {
        if(!$commandId){
            return true;
        }
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];

        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "commandID" => $commandId,

        ];

        $result = $this->request("/v4/cloud-assistant/delete-command",$params,$query,"post");


        return (bool)$result;
    }
    public function cloudAssistantInvokeCommand(string $commandId,array $instanceIds,int $timeout=60, string $regionId="")
    {
        if(!$commandId || empty($instanceIds)){
            return true;
        }
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];

        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "commandID" => $commandId,
            "instanceIDs" => implode($instanceIds),
        ];
        if($timeout > 0 ){
            $params["timeout"] = $timeout;
        }

        $result = $this->request("/v4/cloud-assistant/invoke-command",$params,$query,"post");

        if(!$result){
            return ;
        }

        return UtilArr::get($result,"returnObj.invokedID");
    }
    public function cloudAssistantRunCommand(string $commandContent,array $instanceIds,string $commandType="Shell",int $timeout=60, string $regionId="")
    {
        if(!$commandContent){
            $this->setError("命令内容不能为空");
            return ;
        }
        if( empty($instanceIds)){
            $this->setError("主机ID不能为空");
            return;
        }
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];

        $params=[
            //"clientToken" => $this->comCreateGuid(),
            "regionID" => $regionId,
            "commandType" => $commandType,
            "instanceIDs" => implode($instanceIds),
            "commandContent" => $this->cloudAssistantEncodeCommandContent($commandContent),
            "saveCommand"=>false
        ];
        if($timeout > 0 ){
            $params["timeout"] = $timeout;
        }

        $result = $this->request("/v4/cloud-assistant/run-command",$params,$query,"post");

        if(!$result){
            return ;
        }

        return UtilArr::get($result,"returnObj.invokedID");
    }
    public function cloudAssistantCommandInvocationDescribe(string $invokedId="",string $commandId="", string $regionId="")
    {
        if(!$invokedId && !$commandId){
            return [];
        }
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];

        $params=[
            "regionID" => $regionId,
            "pageSize" => 50,
        ];
        if($invokedId ){
            $params["invokedID"] = $invokedId;
        }
        if($commandId ){
            $params["commandID"] = $commandId;
        }

        $result = $this->request("/v4/cloud-assistant/describe-invocation-results",$params,$query,"post");

        if(!$result){
            return ;
        }

        return UtilArr::get($result,"returnObj.results");
    }



    public function volumeShow(string $diskId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [
            "regionID" => $regionId,
            "diskID" => $diskId,
        ];
        $params=[

        ];
        $result = $this->request("/v4/ecs/volume/show",$params,$query,"get");
        if(!$result){
            return;
        }

        return UtilArr::get($result,"returnObj");
    }
    public function volumeUpdate(string $diskId,string $name,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            "regionID" => $regionId,
            "diskID" => $diskId,
            "diskName" => $name,
        ];
        $result = $this->request("/v4/ecs/volume/update",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }
    public function volumeAttach(string $diskId,string $instanceId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            "regionID" => $regionId,
            "diskID" => $diskId,
            "instanceID" => $instanceId,
        ];
        $result = $this->request("/v4/ecs/volume/attach",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }
    public function volumeDetach(string $diskId,string $instanceId="",string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            "regionID" => $regionId,
            "diskID" => $diskId,
        ];
        if($instanceId){
            $params["instanceID"] = $instanceId;
        }
        $result = $this->request("/v4/ecs/volume/detach",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }
    public function volumeDelete(string $diskId,string $regionId="")
    {
        $regionId = $this->getRegionId($regionId);

        $query = [

        ];
        $params=[
            "regionID" => $regionId,
            "diskID" => $diskId,
            "deleteSnapWithEbs" => true,
        ];
        if($instanceId){
            $params["instanceID"] = $instanceId;
        }
        $result = $this->request("/v4/ecs/volume/delete",$params,$query,"post");
        if(!$result){
            return;
        }

        return (bool)$result;
    }

}
